iT邦幫忙

2024 iThome 鐵人賽

DAY 24
0
自我挑戰組

30 天 vueuse 原始碼閱讀與實作系列 第 24

[Day 24] useElementVisibility

  • 分享至 

  • xImage
  •  

官方 Demo:https://vueuse.org/core/useElementVisibility/

useElementVisibility 算是昨天 useIntersectionObserver 的應用,可以搭配著看。

useElementVisibility 原始碼(簡易版)

// src/compositions/useElementVisibility.js

import { ref } from 'vue'
import { defaultWindow } from '@/helper'
import { useIntersectionObserver } from '@/compositions/useIntersectionObserver'

export function useElementVisibility(element, options = {}) {
  const { window = defaultWindow, scrollTarget, threshold = 0 } = options
  const elementIsVisible = ref(false)

  useIntersectionObserver(
    element,
    ([{ isIntersecting }]) => {
      elementIsVisible.value = isIntersecting
    },
    {
      root: scrollTarget,
      window,
      threshold,
    },
  )

  return elementIsVisible
}

簡易版的部分是指傳入 useIntersectionObserver 的第二個參數 callback function,晚點會貼原始碼的版本。

先大概看一下這段在做的事,當 element 進到瀏覽器 viewport 時(root 為 undefined,預設會找 document viewport),回執行我們傳入的 callback function:

([{ isIntersecting }]) => {
  elementIsVisible.value = isIntersecting
}

最後 return elementIsVisible 出去給上層使用,看官方 Demo 那個右下角的 "inside", "outside",就是拿 elementIsVisible 狀態做判斷的。

目前的 callback function 有什麼問題?

([{ isIntersecting }]) => {
  elementIsVisible.value = isIntersecting
}

根據昨天看 useIntersectionObserver 的 Demo code 原始碼,用法也是上面這種簡易版本,關於 useElementVisibility 的 Demo,我用上面簡易版本的 callback function 實作起來沒遇到什麼問題,所以今天在看到原始碼的時候,覺得為什麼變複雜了 XD。

先來看一下目前版本可能會遇到什麼問題,可以參考官方 PR:https://github.com/vueuse/vueuse/pull/3365
PR 描述大概是在說,我們目前透過解構,固定拿第一個 entry,不一定是最新的。
接著看 mdn 文件關於 entry 排序的說明:

The list of entries received by the callback includes one entry for each threshold-crossing event — multiple entries can be received at a time, either from multiple targets or from a single target crossing multiple thresholds in a short amount of time. The entries are dispatched using a queue, so they should be ordered by the time they were generated, but you should preferably use IntersectionObserverEntry.time to correctly order them.

mdn 連結:https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#intersection_observer_concepts_and_usage

看起來是拿到最新的 entry 需要透過 entry.time 來判斷,知道問題在哪後,來看看原始碼中 callback function 是怎麼實現的:

(intersectionObserverEntries) => {
  let isIntersecting = elementIsVisible.value

  // Get the latest value of isIntersecting based on the entry time
  let latestTime = 0
  for (const entry of intersectionObserverEntries) {
    if (entry.time >= latestTime) {
      latestTime = entry.time
      isIntersecting = entry.isIntersecting
    }
  }
  elementIsVisible.value = isIntersecting
},

每次執行 callback 的時候,會把每個 entry 拿出來檢查 entry.time 是否是目前最大的,是的話代表這次是目前最新的狀態,直到跑完整個 intersectionObserverEntries,就可以得到這次 callback 中最新的 entry,最後更新 elementIsVisible.value。

GitHub PR:https://github.com/RhinoLee/30days_vue/pull/23/files


今天到這邊告一段落~ 明天會繼續看 useElementVisibility 的單元測試是怎麼測的。


上一篇
[Day 23] useIntersectionObserver
下一篇
[Day 25] useElementVisibility - unit test
系列文
30 天 vueuse 原始碼閱讀與實作30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言